package reg.domain;

import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import reg.data.IConfiguracionDAO;
import reg.data.IManifestacionDAO;
import reg.data.IUsuarioDAO;
import reg.data.JDBCConfiguracionDAO;
import reg.data.JDBCManifestacionDAO;
import reg.data.JDBCUsuarioDAO;
import reg.localization.DBPosition;
import reg.localization.IGetPosition;
import reg.localization.Position;


public class FiltroPrioridad{
		
	private static IConfiguracionDAO filtros = new JDBCConfiguracionDAO();
	private static IGetPosition position = new DBPosition();
	private static IManifestacionDAO manifestaciones = new JDBCManifestacionDAO();
	private static IUsuarioDAO usuarios = new JDBCUsuarioDAO();
	
	//Devuelve valor de afinidad dependiente de un filtro
	public static ManifestacionPonderada calculeAfinidadManifestacion(Manifestacion manifestacion, Usuario usuario, String filtro)
	{
		int dias = getDiasManifestacion(manifestacion.getFecha());
		int asistentes = getAsistentes(manifestacion.getIdManifestacion());
		int amigos = getAmigos(manifestacion.getIdManifestacion(),usuario.getIdUsuario());
		int distancia = getDistacia(usuario.getCiudad(), manifestacion.getCiudad());
				
		double ponderacion = calculePonderation(dias, asistentes, amigos, distancia, filtro);
				
		return new ManifestacionPonderada(manifestacion, usuario.getIdUsuario(), ponderacion, dias, asistentes, amigos, distancia, filtro);
	}
	
	//calcula la formula dando el valor de sus etiquetas
	private static double calculePonderation (int dias, int asistentes, int amigos, int distancia, String formula)
	{
		//dividimos y damos por sentado que si esta en la BD el filtro funciona correctamente
		String[] argumentos = formula.split(" ");
		
		double result = 0.0;
		
		for (int i = 0; i < argumentos.length; i++)
		{
			if(i == 0)
			{
				//si es el primero lo asignamos a result
				result = getValorEtiqueta(dias, asistentes, amigos, distancia, argumentos[i]);
			}
			//por cada operador tenemos que buscar su etiqueta
			else if (i % 2 == 1)
			{
				Integer value = getValorEtiqueta(dias, asistentes, amigos, distancia, argumentos[i+1]);
				//decidimos que operacion vamos a aplicarle
				if (argumentos[i].equals("*")) {
					result = result * value;
				}

				else if (argumentos[i].equals("+")) {
					result = result + value;
				}

				else if (argumentos[i].equals("-")) {
					result = result - value;
				}

				else if (argumentos[i].equals("/")) {
					result = value / result;
				}
			}
		}
		return result;
	}
	
	//inserta o edita si el filtro esta bien parseado, si no lo esta devuelve false, si todo ha ido bien true
	public static boolean insertFiltro(String nombreFiltro, String filtro)
	{
		//si parsea guardamos/editamos
		if(ParseoFiltroPrioridad(filtro))
		{
			filtros.insertConfiguracion(nombreFiltro, "filtro", filtro);
			return true;
		}
		else
		{
			return false;
		}
	}
	
	public static String getFiltro(String nameFiltro)
	{
		return filtros.getConfiguracion(nameFiltro, "filtro");
	}
	
	public static void deleteFiltro(String nombreFiltro)
	{
		filtros.deleteConfiguracion(nombreFiltro, "filtro");
	}
	
	public static List<String> getNameFiltros()
	{
		return filtros.getTodasConfiguracionesPorTematica("filtro");
	}
	
	//distancia en kilometros entre las dos ciudades
	private static int getDistacia(String ciudadUser, String ciudadMani)
	{
		Position user = position.getPosicion(ciudadUser);
		Position manif = position.getPosicion(ciudadMani);
		
		double x1 = user.longitude * 111.32;
		double y1 = user.latitude * 110.57;
		double x2 = manif.longitude * 111.32;
		double y2 = manif.latitude * 110.32;
		
		double distancia = Math.sqrt(Math.pow(x2-x1, 2)+Math.pow(y2-y1, 2));
		
		return (int)distancia;
	}
	
	//CASOS DE USO A PARTIR DE LA API
	//devuelve una lista ordenada por ponderacion dependiente de un usuario y un filtro.
	public static List<ManifestacionPonderada> GetManifestacionesPonderadas (int idUsuario, String nombreFiltro)
	{
		//construimos el usuario a partir de su ID (sin amigos)
		Usuario user = usuarios.getUsuarioSinAmigos(idUsuario);
		//cogemos las manifestaciones en las cuales el usuario no se haya definido ninguna asistencia
		List<Manifestacion> manifestacionesSinAsistencias = manifestaciones.manifestacionSinAsistencia(idUsuario);//escribir JDBC
		//cogemos el filtro a partir del objeto de entrada "nombrefiltro"
		String filtro = filtros.getConfiguracion(nombreFiltro, "filtro");
		//a cada manifestacion le calculamos su ponderacion y las vamos guardando en una Lista
		List<ManifestacionPonderada> manifestacionesResult = new LinkedList<ManifestacionPonderada>();
		for(Manifestacion mani: manifestacionesSinAsistencias)
		{
			manifestacionesResult.add(calculeAfinidadManifestacion(mani, user, filtro));
		}
		//Ordenamos de mayor a menor la lista de manifestaciones ponderadas
		Collections.sort(manifestacionesResult);
		Collections.reverse(manifestacionesResult);
		//devuelvo la lista ordenada
		return manifestacionesResult;
	}
	
	//Devuelve el número de dias que queden para que llegue al dia "fecha"
	private static int getDiasManifestacion(Date fecha)
	{
		Date today = Calendar.getInstance().getTime();
		
		return (int)((fecha.getTime()-today.getTime())/(1000*3600*24));
	}
	
	//Devuelve el número de asistentes que van a ir
	private static int getAsistentes(int idManifestacion)
	{
		return manifestaciones.asistenciaManifestacion(idManifestacion);
	}
	
	//Devuelve el numero de amigos del usuario que van a la manifestacion
	private static int getAmigos(int idManifestacion, int idUsuario)
	{
		return manifestaciones.numeroAmigosEnManifestacion(idManifestacion, idUsuario);
	}
	
	//Comprueba si el filtro tiene un parseo correcto y entendible
	private static Boolean ParseoFiltroPrioridad(String filtro)
	{
		//tenemos que comprobar si el texto filtro es valido, para ello se tiene que cumplir que tiene que empezar y terminar por una etiqueta y entre dos etiquetas hay un operador, entre operadores y etiquetas debe haber un espacio
		//separamos el filtro por espacios
		String[] argumentos = filtro.split(" ");
		//analizamos si es <etiqueta> operador
		for(int i= 0 ; i<argumentos.length ; i++)
		{
			//chequeamos que el primero y el ultimo sean etiquetas
			//si es par (etiqueta)
			if(i%2 == 0 || i == 0 || i == argumentos.length-1)
			{
				if(!IsLabel(argumentos[i])) return false;
			}
			
			//si es impar (operador)
			if(i%2 == 1)
			{
				if(!IsOperator(argumentos[i])) return false;
			}
		}
		return true;
	}
	
	//devuelve el valor de una etiqueta pasandole la manifestacion y el usuario por defecto, es un metodo para reducir el codigo del metodo GetAfinidiadPorManifestacion
	private static int getValorEtiqueta(int dias, int asistentes, int amigos, int distancia, String etiqueta)
	{
		if(etiqueta.equals("distancia"))
		{
			return distancia;
		}
		else if(etiqueta.equals("dias"))
		{
			return dias;
		}
		else if(etiqueta.equals("asistentes"))
		{
			return asistentes;
		}
		else if(etiqueta.equals("amigos"))
		{
			return amigos;
		}
		return -1;
	}
	
	//Devuelve true si es operador
	private static boolean IsOperator(String str){
		return str.equals("*")||str.equals("+")||str.equals("-")||str.equals("/");
	}
	
	//Devuelve true si es etiqueta
	private static boolean IsLabel(String str){
		return str.equals("distancia")||str.equals("dias")||str.equals("asistentes")||str.equals("amigos");
	}
}